using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


//Physics Includes
using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Collision;
using GLEED2D;

namespace Urban_Flash.Dependencies
{
    public class PullyBand
    {
        #region Fields
        SliderJoint band;
        #endregion

        #region Constructors
        public PullyBand(World physics, ref Body bodyA, ref Body bodyB)
        {
            band = JointFactory.CreateSliderJoint(physics, bodyA, bodyB, Vector2.Zero, Vector2.Zero, 0.0f, 1.0f);
            band.CollideConnected = false;
            SetBand(0.5f, 0.3f, 0.15f);
        }
        #endregion

        #region Methods
        /// <summary>
        /// Set the Properties of the pully-band
        /// </summary>
        /// <param name="RetractSpeed"> Sets how fast the band will pull back together (Low #'s i.e. 0.5f = Slow speed)</param>
        /// <param name="RetractStrength">Sets how strong the band is. (Low #'s i.e. 0.1f = high elasticity)</param>
        /// <param name="MaxLength">The max length of the band in meters (1.0f = 1 meter whichs eqales 100 pixels in game)</param>
        public void SetBand(float RetractSpeed, float RetractStrength, float MaxLength)
        {
            band.Frequency = RetractSpeed;
            band.DampingRatio = RetractStrength;
            band.MaxLength = MaxLength;
        }
        #endregion
    }

    public class RubberBand
    {
        #region Fields
        DistanceJoint band;
        #endregion

        #region Constructors
        public RubberBand(World physics, ref Body bodyA, ref Body bodyB)
        {
            band = JointFactory.CreateDistanceJoint(physics, bodyA, bodyB, Vector2.Zero, Vector2.Zero);
            band.CollideConnected = false;
            //Play with these values for different effects
            band.DampingRatio = 0.1f;
            band.Frequency = 30.0f;
            band.Length = 2.0f;

        }
        #endregion
    }

    public class StaticPhysicsObject : PhysicsObject
    {
        #region Constructors
        public StaticPhysicsObject(World physics, Vector2 position, float width, float height, Texture2D texture)
            : base(physics, position, width, height, 10, texture)
        {
            body.Friction = 5.0f;
            body.IsStatic = true;
        }

        public StaticPhysicsObject(World physics, Vector2 position, float width, float height)
            : base(physics, position, width, height, 10)
        {
            body.Friction = 1.0f;
            body.IsStatic = true;
        }
        #endregion
    }

    public class PhysicsObject : Item
    {
        #region Constructors
        public PhysicsObject(World physics, Vector2 position, float width, float height, float mass, Texture2D texture)
        {
            this.texture = texture;
            this.origin = new Vector2(texture.Width / 2, texture.Height / 2);
            this.width = width;
            this.height = height;

            SetupPhysics(physics, position, width, height, mass);
        }

        public PhysicsObject(World physics, Vector2 position, float width, float height, float mass)
        {
            this.texture = null;
            this.origin = new Vector2(width / 2, height / 2);
            this.width = width;
            this.height = height;

            SetupPhysics(physics, position, width, height, mass);
        }
        #endregion

        #region Methods
        protected virtual void SetupPhysics(World physics,
            Vector2 position, float width, float height, float mass)
        {
            //Convert Variables to meters for Farseer to manipulate
            float widthMeters = ConvertUnits.ToSimUnits(width);
            float heightMeters = ConvertUnits.ToSimUnits(height);
            Vector2 positionMeters = ConvertUnits.ToSimUnits(position);

            body = BodyFactory.CreateRectangle(physics, widthMeters, heightMeters, mass);
            body.Position = positionMeters;
            body.BodyType = BodyType.Dynamic;

            fixture = FixtureFactory.AttachRectangle(widthMeters, heightMeters, 0.0f, Vector2.Zero, body);
            fixture.Restitution = 0.0f;
            fixture.Friction = 0.0f;
        }


        /// <summary>
        /// Draws this physics object
        /// </summary>        
        /// <param name="spriteBatch">SpriteBatch used to draw this object, should've already started.</param>
        public virtual void Draw(SpriteBatch spriteBatch)
        {
            if (!Visible) return;
            spriteBatch.Draw(texture, ConvertUnits.ToDisplayUnits(body.Position), null, Color.White, body.Rotation, origin, 1.0f, SpriteEffects.None, 0f);

            //Used to scale the texture to the actual size of the object
            //spriteBatch.Draw(texture, new Rectangle((int)fixture.Body.Position.X, (int)fixture.Body.Position.Y, (int)width, (int)height), null, Color.White, body.Rotation, origin, SpriteEffects.None, 0f);

        }

        public virtual void Update(GameTime gameTime)
        {

        }

        /// <summary>
        /// Never Set position it is all handled by the physics engine
        /// </summary>
        public virtual Vector2 Position //Hides Item Position purposely
        {
            get
            {
                return body.Position;
            }
            set
            {
                body.Position = value;
            }
        }
        #endregion

        #region Fields
        protected float width;
        protected float height;
        public Body body;
        public Fixture fixture;
        protected Texture2D texture;
        protected Vector2 origin;
        #endregion
    }

   public static class PhysicsHelper
    {
        public static float GetMaxLinearVelocity(float mass, float linearDamping, float timeDelta, float maxForceLength)
        {
            float maxLinearVelocity = maxForceLength * (1 - timeDelta * linearDamping) / (mass * linearDamping);
            return maxLinearVelocity;
        }

        public static float GetMaxLinearVelocity(float mass, float deltaTime, float linearDamping, Vector2 maxForce)
        {
            return GetMaxLinearVelocity(mass, deltaTime, linearDamping, maxForce.Length());
        }

        public static float GetMaxForceLength(float mass, float deltaTime, float linearDamping, float maxVelocity)
        {
            float maxForceLength = (maxVelocity * mass * linearDamping) / (1 - deltaTime * linearDamping);
            return maxForceLength;
        }

        public static float GetLinearDamping(float mass, float deltaTime, float maxVelocity, float maxForceLength)
        {
            float linearDamping = (maxForceLength) / (maxVelocity * mass + deltaTime * maxForceLength);
            return linearDamping;
        }

        public static float GetLinearDamping(float mass, float deltaTime, float maxVelocity, Vector2 maxForce)
        {
            return GetLinearDamping(mass, deltaTime, maxVelocity, maxForce.Length());
        }
    }
 
}
